#!/bin/groovy
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

// Location of the CN executor node
def cn_ci_host = params.Host_CN_CI_Server
def cn_ci_resource = params.DockerContainers
def upstreamEvent = false

// Variables that can used in main pipeline and in functions
DEFAULT=-1
NRF=0
AMF=1
SMF=2
UPF=3
AUSF=4
UDM=5
UDR=6
NSSF=7
UPF_VPP=8
PCF=9
NORTH=10

// Default tags / branches  --> could be passed on by upstream job
imageNames = ["oai-nrf", "oai-amf", "oai-smf", "oai-upf", "oai-ausf", "oai-udm", "oai-udr", "oai-nssf", "oai-upf-vpp", "oai-pcf"]
repoNames = ["oai-cn5g-nrf", "oai-cn5g-amf", "oai-cn5g-smf", "oai-cn5g-upf", "oai-cn5g-ausf", "oai-cn5g-udm", "oai-cn5g-udr", "oai-cn5g-nssf", "oai-cn5g-upf-vpp", "oai-cn5g-pcf"]
branchNames = ["", "", "", "", "", "", "", "", "", ""]
imageTags = ["", "", "", "", "", "", "", "", "", ""]
upstreamJobs = [false, false, false, false, false, false, false, false, false, false]
pulledImages = [false, false, false, false, false, false, false, false, false, false]


// Which OMEC-GNBSIM TAG to use
ngapTesterTag = params.ngapTesterTag
upstreamTagToUse = params.upstreamTagToUse

rfTags = ""

// TODO: once 5gcsdk is public, not required anymore.
def gitlab_credentials = params.Private_GitLab_Repo_Credentials

//-------------------------------------------------------------------------------
// Pipeline start
pipeline {
  agent {
    label cn_ci_host
  }
  options {
    disableConcurrentBuilds()
    timestamps()
    ansiColor('xterm')
    lock(cn_ci_resource)
  }
  stages {
    stage ('Verify Parameters') {
      steps {
        script {
          echo '\u2705 \u001B[32mVerify Parameters\u001B[0m'

          JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"'
          JOB_TIMESTAMP = JOB_TIMESTAMP.trim()

          echo "Node       is ${NODE_NAME}"

          // Find out the cause of the trigger
          for (cause in currentBuild.getBuildCauses()) {
            if (cause.toString().contains('UpstreamCause')) {
              upstreamEvent = true
            }
            if (cause.toString().contains('OAI-CN5G-NRF')) {
              upstreamJobs[NRF] = true
            }
            if (cause.toString().contains('OAI-CN5G-AMF')) {
              upstreamJobs[AMF] = true
            }
            if (cause.toString().contains('OAI-CN5G-SMF')) {
              upstreamJobs[SMF] = true
            }
            if (cause.toString().contains('OAI-CN5G-UPF')) {
              upstreamJobs[UPF] = true
            }
            if (cause.toString().contains('OAI-CN5G-AUSF')) {
              upstreamJobs[AUSF] = true
            }
            if (cause.toString().contains('OAI-CN5G-UDM')) {
              upstreamJobs[UDM] = true
            }
            if (cause.toString().contains('OAI-CN5G-UDR')) {
              upstreamJobs[UDR] = true
            }
            if (cause.toString().contains('OAI-CN5G-NSSF')) {
              upstreamJobs[NSSF] = true
            }
            if (cause.toString().contains('OAI-CN5G-UPF-VPP')) {
              upstreamJobs[UPF_VPP] = true
            }
            if (cause.toString().contains('OAI-CN5G-PCF')) {
              upstreamJobs[PCF] = true
            }
          }

          sh "git clean -x -d -ff > /dev/null 2>&1"
          sh "git submodule foreach --recursive 'git clean -x -d -ff' > /dev/null 2>&1"
          sh "git submodule deinit --force --all > /dev/null 2>&1"
          // For any upstream job (PR or post-merge), let run on certified tag.
          if (upstreamEvent) {
            sh 'git checkout -f ' + upstreamTagToUse
          }
          sh 'git submodule update --init --recursive ci-scripts/common'
          sh "mkdir -p archives"

          // Having the possibility to run manually as in CI
          int emulation_run = params.EMULATE_MR_RUN.toInteger()
          switch (emulation_run) {
            case NRF:
              upstreamEvent = true
              upstreamJobs[NRF] = true
              break
            case AMF:
              upstreamEvent = true
              upstreamJobs[AMF] = true
              break
            case SMF:
              upstreamEvent = true
              upstreamJobs[SMF] = true
              break
            case UPF:
              upstreamEvent = true
              upstreamJobs[UPF] = true
              break
            case AUSF:
              upstreamEvent = true
              upstreamJobs[AUSF] = true
              break
            case UDM:
              upstreamEvent = true
              upstreamJobs[UDM] = true
              break
            case UDR:
              upstreamEvent = true
              upstreamJobs[UDR] = true
              break
            case NSSF:
              upstreamEvent = true
              upstreamJobs[NSSF] = true
              break
            case PCF:
              upstreamEvent = true
              upstreamJobs[PCF] = true
              break
            case NORTH:
              rfTags = rfTags + ' -i North '
              break
            case DEFAULT:
              break
          }

          for (ii = 0; ii < imageNames.size(); ii++) {
            if (ii == NRF) {
              branchNames[NRF] = params.NRF_BRANCH
              imageTags[NRF] = params.NRF_TAG
              if (upstreamEvent && upstreamJobs[NRF]) {
                rfTags = rfTags + ' -i NRF '
                echo "Upstream Job passed NRF_TAG to use: ${imageTags[NRF]}"
                echo "Upstream Job passed NRF_BRANCH to use: ${branchNames[NRF]}"
              }
            }
            if (ii == AMF) {
              branchNames[AMF] = params.AMF_BRANCH
              imageTags[AMF] = params.AMF_TAG
              if (upstreamEvent && upstreamJobs[AMF]) {
                rfTags = rfTags + ' -i AMF '
                echo "Upstream Job passed AMF_TAG to use: ${imageTags[AMF]}"
                echo "Upstream Job passed AMF_BRANCH to use: ${branchNames[AMF]}"
              }
            }
            if (ii == SMF) {
              branchNames[SMF] = params.SMF_BRANCH
              imageTags[SMF] = params.SMF_TAG
              if (upstreamEvent && upstreamJobs[SMF]) {
                rfTags = rfTags + ' -i SMF '
                echo "Upstream Job passed SMF_TAG to use: ${imageTags[SMF]}"
                echo "Upstream Job passed SMF_BRANCH to use: ${branchNames[SMF]}"
              }
            }
            if (ii == UPF) {
              branchNames[UPF] = params.UPF_BRANCH
              imageTags[UPF] = params.UPF_TAG
              if (upstreamEvent && upstreamJobs[UPF]) {
                rfTags = rfTags + ' -i UPF '
                echo "Upstream Job passed UPF_TAG to use: ${imageTags[UPF]}"
                echo "Upstream Job passed UPF_BRANCH to use: ${branchNames[UPF]}"
              }
            }
            if (ii == AUSF) {
              branchNames[AUSF] = params.AUSF_BRANCH
              imageTags[AUSF] = params.AUSF_TAG
              if (upstreamEvent && upstreamJobs[AUSF]) {
                rfTags = rfTags + ' -i AUSF '
                echo "Upstream Job passed AUSF_TAG to use: ${imageTags[AUSF]}"
                echo "Upstream Job passed AUSF_BRANCH to use: ${branchNames[AUSF]}"
              }
            }
            if (ii == UDM) {
              branchNames[UDM] = params.UDM_BRANCH
              imageTags[UDM] = params.UDM_TAG
              if (upstreamEvent && upstreamJobs[UDM]) {
                rfTags = rfTags + ' -i UDM '
                echo "Upstream Job passed UDM_TAG to use: ${imageTags[UDM]}"
                echo "Upstream Job passed UDM_BRANCH to use: ${branchNames[UDM]}"
              }
            }
            if (ii == UDR) {
              branchNames[UDR] = params.UDR_BRANCH
              imageTags[UDR] = params.UDR_TAG
              if (upstreamEvent && upstreamJobs[UDR]) {
                rfTags = rfTags + ' -i UDR '
                echo "Upstream Job passed UDR_TAG to use: ${imageTags[UDR]}"
                echo "Upstream Job passed UDR_BRANCH to use: ${branchNames[UDR]}"
              }
            }
            if (ii == NSSF) {
              branchNames[NSSF] = params.NSSF_BRANCH
              imageTags[NSSF] = params.NSSF_TAG
              if (upstreamEvent && upstreamJobs[NSSF]) {
                rfTags = rfTags + ' -i NSSF '
                echo "Upstream Job passed NSSF_TAG to use: ${imageTags[NSSF]}"
                echo "Upstream Job passed NSSF_BRANCH to use: ${branchNames[NSSF]}"
              }
            }
            if (ii == UPF_VPP) {
              branchNames[UPF_VPP] = params.UPF_VPP_BRANCH
              imageTags[UPF_VPP] = params.UPF_VPP_TAG
              if (upstreamEvent && upstreamJobs[UPF_VPP]) {
                rfTags = rfTags + ' -i UPF_VPP '
                echo "Upstream Job passed UPF_VPP_TAG to use: ${imageTags[UPF_VPP]}"
                echo "Upstream Job passed UPF_VPP_BRANCH to use: ${branchNames[UPF_VPP]}"
              }
            }
            if (ii == PCF) {
              branchNames[PCF] = params.PCF_BRANCH
              imageTags[PCF] = params.PCF_TAG
              if (upstreamEvent && upstreamJobs[PCF]) {
                rfTags = rfTags + ' -i PCF '
                echo "Upstream Job passed PCF_TAG to use: ${imageTags[PCF]}"
                echo "Upstream Job passed PCF_BRANCH to use: ${branchNames[PCF]}"
              }
            }
          }

          // On Ubuntu servers, we shall pull from private local registry
          if ((cn_ci_host == 'Selfix') || (cn_ci_host == 'Alambix')) {
            pullFromSelfix = true
            try {
              // Login
              sh 'docker login -u oaicicd -p oaicicd selfix.sboai.cs.eurecom.fr > /dev/null 2>&1'
            } catch (Exception e) {
              echo 'Problem w/ selfix registry. Let see if we can use local images'
              pullFromSelfix = false
            }
            if (pullFromSelfix) {
              // We will try to pull from Selfix registry.
              // If the NF CI is yet ready, it's OK, it will fail but keep working with local registry
              for (ii = 0; ii < imageNames.size(); ii++) {
                imageTags[ii] = pullImageFromSelfix(ii, imageNames[ii], imageTags[ii], branchNames[ii])
              }
              pullImageFromSelfix(-1, 'ngap-tester', ngapTesterTag, 'develop')
              // Logout
              sh 'docker logout selfix.sboai.cs.eurecom.fr > /dev/null 2>&1'
            }
          }
          if (cn_ci_host == 'Alambix') {
            // Forcing to run on Northbound testing on Alambix
            // TODO: remove this when UPF-VPP runs again on Ubuntu-24
            rfTags = '-i North '
          }

          // Verify that the images are available
          imageStatus = 0
          for (ii = 0; ii < imageNames.size(); ii++) {
            imageStatus += checkImageInfo(imageNames[ii], imageTags[ii])
          }
          imageStatus += checkImageInfo('ngap-tester', ngapTesterTag)
          if (imageStatus > 0) {
            error ("Some images are not present!")
          }
        }

        // TODO: once the 5gcsdk repo is public, no longer needed.
        // A git submodule approach would be better.
        sh "rm -Rf test/5gcsdk"
        sh "mkdir -p test/5gcsdk"
        dir ('test/5gcsdk') {
          checkout scmGit(
            branches: [[name: '*/develop-testing']], 
            extensions: [], 
            userRemoteConfigs: [[
            credentialsId: gitlab_credentials,
            url: 'https://gitlab.eurecom.fr/5gc/5gcsdk.git']])
        }

        // Making sure that the latest version of oai-gnb and oai-nr-ue develop images
        // is on the test server
        sh "docker pull oaisoftwarealliance/oai-gnb:develop || true"
        sh "docker pull oaisoftwarealliance/oai-nr-ue:develop || true"
        sh "docker image prune -f"
      }
    }
    stage ('Run Robot Framework Tests') {
      steps {
        script {
          sh 'cp -Rf /opt/ngap-tester-robot/* test/template/'

          sh 'sed -i -e "s@ngap-tester:develop@ngap-tester:' + ngapTesterTag + '@" test/image_tags.py'
          for (ii = 0; ii < imageTags.size(); ii++) {
            prepareRobotImageTags(imageNames[ii], imageTags[ii])
          }
          sh 'robot ' + rfTags + ' --outputdir archives test'
        }
      }
    }
  }
  post {
    always {
      script {
        // Zipping all archived log files
        if (fileExists('archives_ngap')) {
          sh 'zip -r -qq cn5g_fed_ngap_tests.zip archives_ngap'
          // Currently no public artifacting on the logs and pcap of NGAP tester
          sh 'chmod 666 cn5g_fed_ngap_tests.zip'
          sh 'cp cn5g_fed_ngap_tests.zip /opt/ngap-tester-logs/cn5g_fed-${JOB_NAME}-${BUILD_ID}.zip'
        }
        if (fileExists('archives')) {
          sh 'zip -r -qq cn5g_fed_robot_tests.zip archives'
          archiveArtifacts artifacts: 'cn5g_fed_robot_tests.zip'
        }
        // archive robot framework log
        if (fileExists('archives/log.html')) {
          archiveArtifacts artifacts: 'archives/log.html, archives/report.html'
        }
        // We generate HTML report if we did not already
        // we shall not fail in post section
        if (!fileExists('test_results_robot_framework.html')) {
          sh './ci-scripts/checkRobotFrameworkHtmlReport.py --job_name ' + JOB_NAME + ' --job_id ' + BUILD_ID + ' --job_url ' + BUILD_URL + ' || true'
        }
        if (fileExists('test_results_robot_framework.html')) {
          if (JOB_NAME == 'OAI-CN5G-RobotTest-NorthBound-Testing') {
            sh 'mv test_results_robot_framework.html test_results_robot_framework_northbound.html'
            archiveArtifacts artifacts: 'test_results_robot_framework_northbound.html'
          } else {
            archiveArtifacts artifacts: 'test_results_robot_framework.html'
          }
        }
      }
    }
    cleanup {
      script {
        sh 'docker volume prune --force'
        // Removing the images that we pulled.
        for (ii = 0; ii < imageNames.size(); ii++) {
          if (pulledImages[ii]) {
            sh 'docker rmi ' + imageNames[ii] + ':' + imageTags[ii]
          }
        }
      }
    }
  }
}

def pullImageFromSelfix(idx, imageName, imageTag, branchName) {
  if ((imageTag == 'develop') && (branchName == 'develop')) {
    try {
      tag = sh returnStdout: true, script: './ci-scripts/retrieveLatestTagOnPrivateRepo.py --repo-name ' + imageName
      tag = tag.trim()
    } catch (Exception e) {
      return imageTag
    }
  } else {
    tag = imageTag
  }
  // We may have wrong image tag?
  try {
    sh 'docker pull selfix.sboai.cs.eurecom.fr/' + imageName + ':' + tag
    sh 'docker image tag selfix.sboai.cs.eurecom.fr/' + imageName + ':' + tag + ' ' + imageName + ':' + tag
    sh 'docker rmi selfix.sboai.cs.eurecom.fr/' + imageName + ':' + tag
    if (idx != -1) {
      pulledImages[idx] = true
    }
  } catch (Exception e) {
    echo "${imageName} Image tag to test (${imageName}:${tag} does not exist on selfix private registry!"
    tag = imageTag
  }
  return tag
}

def checkImageInfo(imageName, origTag) {
  status = 0
  sh "echo 'Tested Tag is ${imageName}:${origTag}' > archives/${imageName}-image-info.log"
  try {
    sh "docker image inspect --format='Size = {{.Size}} bytes' ${imageName}:${origTag} >> archives/${imageName}-image-info.log"
    sh "docker image inspect --format='Date = {{.Created}}' ${imageName}:${origTag} >> archives/${imageName}-image-info.log"
  } catch (Exception e) {
    echo "${imageName} Image tag to test (${imageName}:${origTag} does not exist!"
    status = 1
  }
  return status
}

def prepareRobotImageTags(imageName, tag) {
   sh 'sed -i -e "s@oaisoftwarealliance/' + imageName + ':develop@' + imageName + ':' + tag + '@" test/image_tags.py'
}
